package com.util;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;

import java.io.File;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 扫描项目下指定位置处的class文件
 */
@Slf4j
public class ScanUtil {
    /**
     * 获取项目根路径
     */
    static String rootPath;
    static{
        try {
            rootPath = ScanUtil.class.getResource("/").toURI().getPath();
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }
        log.info("项目根路径："+rootPath);
    }
    //C:\minlch\TeachingSpace\ServletMVC_Vue_Demo\target\classes
    //C:\minlch\TeachingSpace\ServletMVC_Vue_Demo\target\classes\   com.util.ScanUtil
    /**
     * 扫描指定路径下包含子文件夹下的所有的class文件
     * @param packageName
     * @return
     */
    public static Set<Class> scanPackage(String packageName) { // com.servlet.XXXX.class  com/servlet/XXXX.class
        //获取相对路径
        String packegPath = packageName.replace('.', '/').replace("/class", ".class");
        //创建绝对路径的file对象
        File dir = new File(rootPath, packegPath);
        if (!dir.exists()) {
            dir = new File(rootPath, packegPath + ".class");
            if (!dir.exists()) {
                throw new RuntimeException("path参数:" + packageName + ",指定对象不存在!!!" + dir.getName());
            }
        }
        Set<Class> set = new HashSet<>();
        //检查dir对象是否时一个目录
        if (dir.isDirectory()) {
            //获取文件夹下所有的子文件夹和class文件
            File[] fileList = dir.listFiles(file -> file.isDirectory() || file.getName().endsWith(".class"));
            for (File file : fileList) {
                try {
                    //如果是目录就递归扫描
                    if (file.isDirectory()) {
                        String temPackageName = file.getPath().replace(rootPath, "").replace('\\', '.');
                        Set<Class> temSet = scanPackage(temPackageName);
                        set.addAll(temSet);
                        continue;
                    }
                    //拼写类的全路径名
                    String fileName = packageName + "." + file.getName().replace(".class", "");
                    //使用Class.forName方法加载类
                    set.add(Class.forName(fileName));
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        } else {
            if (packageName.endsWith(".class")) {
                packageName = packageName.replace(".class", "");
            }
            //如果是文件就做添加处理
            try {
                set.add(Class.forName(packageName));
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return set;
    }

    /**
     * 扫描指定路径下所有具有指定注解的class文件
     * @param packegName
     * @param annotatedClass
     * @return
     */
    public static Set<Class> scanPackage4AnnotatedClass(String packegName, Class annotatedClass) {
        Set<Class> set = scanPackage(packegName);
        if(annotatedClass == null) {
            return set;
        }
        Set<Class> resultSet = new HashSet<>();
        //遍历，搜寻所有具有指定注解的class类
        for (Class clazz : set) {
            //检查类上是否有指定的注解
            if (clazz.getAnnotation(annotatedClass) != null) {
                resultSet.add(clazz);
            }
        }
        return resultSet;
    }

    /**
     * 扫描指定路径下所有具有指定父类或父接口的class文件
     *
     * @param packegName
     * @param typeClass
     * @return
     */
    public static Set<Class> scanPackage4TypeClass(String packegName, Class typeClass) {
        //获取指定路径下所有class类
        Set<Class> set = scanPackage(packegName);
        if (typeClass == null) {
            return set;
        }
        Set<Class> resultSet = new HashSet<>();
        //遍历，搜寻继承指定父类的类
        for (Class clazz : set) {
            //获取当前类的所有父类和父接口
            List<Class> list = Arrays.asList(clazz.getInterfaces());
            if (list.contains(typeClass) || typeClass.equals(clazz.getSuperclass())) {
                resultSet.add(clazz);
            }
        }
        return resultSet;
    }



    @Test
    public void testMain(){

        Set<Class> classes = ScanUtil.scanPackage("com.controller");
        for(Class clazz : classes){
            System.out.println(clazz.getName());
        }

        System.out.println();

        Set<Class> classes1 = ScanUtil.scanPackage4AnnotatedClass("com.controller", Controller.class);
        for(Class clazz : classes1){
            System.out.println(clazz.getName());
        }
    }
}